﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using ConsolidationTool.Common.Entities;

namespace ConsolidationTool.Common
{

    public interface IMergeProcess<T>
    {
        string Title { get; set; }
        string RegionCode { get; set; }
        string Year { get; set; }
        string Month { get; set; }
        string ForceRateType { get; set; }
        string ForceRate { get; set; }
        string OriginalTitle { get; }
        bool isKoMode { get; set; }
        bool WithoutYTDM1 { get; set; }

        List<T> Rows { get; set; }
        List<T> Process(List<CacheSPItem> AllItemsOfRegion, List<CacheSPItem> InternalSalesItems);
        List<T> UnKoProcess(List<CacheSPItem> AllItemsOfRegion, List<CacheSPItem> InternalSalesItems);
    }

    [Serializable]
    public class BaseMerge<T> : IMergeProcess<T>
    {
        public string Title { get; set; }
        public string RegionCode { get; set; }
        public string Year { get; set; }
        public string Month { get; set; }
        public string ForceRateType { get; set; }
        public string ForceRate { get; set; }
        public bool isKoMode { get; set; }
        public string InputType
        {
            get
            {
                return Title.Split('_')[1];
            }
        }
        public string OriginalTitle
        {
            get
            {
                if (!isKoMode)
                {
                    return Title.Split('_').Last();
                }
                return Title.Substring(2);
            }
        }
        public bool WithoutYTDM1 { get; set; }

        private string ColorHightLight(decimal value)
        {
            var temp = value.ToString("N1");
            if (value > 0)
                return "<b style='color:green'>" + temp + "</b>";
            else if (value < 0)
                return "<b style='color:red'>" + temp + "</b>";

            return temp;
        }
        private decimal FixExchangeRate(decimal value, string er)
        {
            return FixExchangeRate(value, BusinessService.decimalParse(er));
        }
        private decimal FixExchangeRate(decimal value, decimal er)
        {
            return Utilities.Division(value, er);
        }

        public virtual List<T> Rows { get; set; }

        public virtual List<T> Process(List<CacheSPItem> AllItemsOfRegion, List<CacheSPItem> InternalSalesItems)
        {
            bool isBudgetRow = Rows[0] is BudgetMergeRow;
            bool isForecastRow = Rows[0] is ForecastMergeRow;

            Rows.Cast<BaseMergeRow>().ToList().ForEach(p =>
            {
                if (p.Process == null && p.datarow)
                {
                    List<string> properties = p.GetType().GetProperties(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public).Select(a => a.Name).ToList();

                    decimal CMTmp = 0;

                    //push a internal sales condation
                    if (p.internalsales != null)
                    {
                        properties.ForEach(pro =>
                        {
                            p.internalsales.BudgetCalc(InternalSalesItems, pro, isBudgetRow ? "Budget_CM" : pro, out CMTmp);
                            if (isForecastRow)
                                p.Log += string.Format("<b style='color:green;'>InternalSales</b>({1}):CM:({0})<br/>", ColorHightLight(CMTmp), p.internalsales.entity);
                            p.TrySetValue(pro, CMTmp); //set a default value to minus internal sales
                        });
                    }

                    var mergeItems = AllItemsOfRegion;
                    if (p.entityFilter != null)
                        mergeItems = AllItemsOfRegion.Where(c => p.entityFilter.Contains(c.CoName)).ToList();

                    mergeItems.ForEach(item =>
                    {
                        //if (isForecastRow)
                        //    p.Log += string.Format("<b>({0})</b>({1})", item.CoName, item.LocalCurrency);
                        var row = item.KoFields.FirstOrDefault(c => c.accountcode == p.accountcode);

                        properties.ForEach(pro =>
                        {
                            var oldValue = BusinessService.decimalParse(row.TryGetValue(pro) + "");
                            var newValue = BusinessService.decimalParse(p.TryGetValue(pro) + "");

                            //process RBNW divsion 1000
                            if (p.convertT.IndexOf(item.CoName) != -1) {
                                oldValue = oldValue / 1000;
                            }

                            //if (isForecastRow)
                            //    p.Log += string.Format(" Original:[{0}:({1})]", pro, ColorHightLight(oldValue));

                            if (p.exchangerate)
                            {
                                string exchangeRateField = isBudgetRow ? "Budget_CM" : pro;

                                var exchangeRate = item.ExchangeRate.FirstOrDefault(c => c.From.Equals(InputType + "-Original"));
                                if (p.raterole != null && !pro.Contains(p.raterole.skip)) //添加对需要取到其它表的汇率进行支持
                                {
                                    exchangeRate = item.ExchangeRate.FirstOrDefault(c => c.From == string.Format("{0}-{1}", p.raterole.table, p.raterole.mode));
                                    exchangeRateField = p.raterole.field.IsNotNullOrEmpty() ? p.raterole.field : exchangeRateField;
                                }
                                else
                                    exchangeRateField = ForceRate.IsNullOrEmpty() ? exchangeRateField : ForceRate ;

                                var fieldExchangeRate = exchangeRate.TryGetValue(exchangeRateField);
                                newValue += FixExchangeRate(oldValue, fieldExchangeRate + "");

                                //if (isForecastRow)
                                //    p.Log += string.Format(" / <b>After:</b>[{0}:({1})], Rate:[{2}] <br/>", pro, ColorHightLight(newValue), fieldExchangeRate);
                            }
                            else
                            {
                                newValue += oldValue;
                                //if (isForecastRow)
                                //    p.Log += string.Format(" / <b>After:</b>[{0}:({1})]<br/>", pro, ColorHightLight(newValue));
                            }

                            p.TrySetValue(pro, newValue);
                        });
                    });


                }
            });

            return Rows;
        }

        public virtual List<T> UnKoProcess(List<CacheSPItem> AllItemsOfRegion, List<CacheSPItem> InternalSalesItems)
        {
            var oldRow = Rows.Cast<ActualMergeRow>().ToList();

            oldRow.ForEach(p =>
            {
                if (p.Process == null && p.datarow)
                {
                    decimal CMTmp = 0, YTDM1Tmp = 0, YTDTmp = 0;

                    //push a internal sales condation
                    if (p.internalsales != null)
                    {
                        p.internalsales.Calc(InternalSalesItems, out CMTmp, out YTDM1Tmp, out YTDTmp);
                        p.Log += string.Format("<b style='color:green;'>InternalSales</b>({3}):CM:({0}),YTDM1:({1}),YTD:({2})<br/>", ColorHightLight(CMTmp), ColorHightLight(YTDM1Tmp), ColorHightLight(YTDTmp), p.internalsales.entity);
                    }
                    var mergeItems = AllItemsOfRegion;

                    if (p.entityFilter != null)
                        mergeItems = AllItemsOfRegion.Where(c => p.entityFilter.Contains(c.CoName)).ToList();

                    mergeItems.ForEach(item =>
                    {
                        //just beacuse log
                        var OriginalCMItemValue = 0.00M;
                        var OriginalYTDM1ItemValue = 0.00M;
                        var OriginalYTDItemValue = 0.00M;

                        var CMItemValue = item.GetAfterRateValue(p, "CM", ForceRate, out OriginalCMItemValue);
                        var YTDItemValue = item.GetAfterRateValue(p, "YTD", ForceRate, out OriginalYTDItemValue);


                        p.Log += string.Format("<b>({0})</b>({3}) Original:[C:({1}),Y:({2})],changeRate:{4}", item.CoName, ColorHightLight(OriginalCMItemValue), ColorHightLight(OriginalYTDItemValue), item.LocalCurrency, p.exchangerate);


                        p.Log += string.Format(" / <b>After:</b>[C:({0}),Y:({1})], Rate:[C:{2},Y:{3}] <br/>", ColorHightLight(CMItemValue), ColorHightLight(YTDItemValue), item.ExchangeRate.FirstOrDefault().CM, item.ExchangeRate.FirstOrDefault().YTD);
                        //}

                        CMTmp += CMItemValue;
                        if (!WithoutYTDM1)
                        {
                            var YTDM1ItemValue = item.GetAfterRateValue(p, "YTDM1", "YTDM_1", out OriginalYTDM1ItemValue);
                            YTDM1Tmp += YTDM1ItemValue;
                        }
                        YTDTmp += YTDItemValue;
                    });

                    p.CM = CMTmp;
                    p.YTDM_1 = YTDM1Tmp;
                    p.YTD = YTDTmp;
                }
            });

            return oldRow.Cast<T>().ToList();
        }

        public BaseMerge<BaseMergeRow> Convert()
        {
            var newType = new BaseMerge<BaseMergeRow>();

            newType.Rows = this.Rows.Cast<BaseMergeRow>().ToList();
            newType.Title = this.Title;
            newType.RegionCode = this.RegionCode;
            newType.Year = this.Year;
            newType.Month = this.Month;
            newType.ForceRate = this.ForceRate;
            newType.ForceRateType = this.ForceRateType;
            newType.isKoMode = this.isKoMode;
            newType.WithoutYTDM1 = this.WithoutYTDM1; 
            return newType;
        }
    }



    [Serializable]
    public class ActualMerge : BaseMerge<ActualMergeRow>
    {
    }
    [Serializable]
    public class BudgetMerge : BaseMerge<BudgetMergeRow>
    {
    }
    [Serializable]
    public class ForecastMerge : BaseMerge<ForecastMergeRow>
    {
    }

    [Serializable]
    public class BaseMergeRow
    {
        public BaseMergeRow()
        {
            exchangerate = true;
            convertT = string.Empty;
        }
        public string accountcode { get; set; }
        public string description { get; set; }
        public string unit { get; set; }
        public int digits { get; set; }

        public bool sumentity { get; set; }
        public bool exchangerate { get; set; }
        public InternalSale internalsales { get; set; }
        public List<string> entityFilter { get; set; }
        public string convertT { get; set; }
        public string forcefield { get; set; }

        public RateRole raterole { get; set; }

        //public object afterBinding { get; set; }

        public bool bold { get; set; }
        public bool emptyrow { get; set; }
        public bool controlrow { get; set; }
        public bool insertcontrol { get; set; }

        public bool datarow { get; set; }
        public int index { get; set; }

        public List<Process> Process { get; set; }

        public string Log { get; set; }

    }

    [Serializable]
    public class ActualMergeRow : BaseMergeRow
    {
        public decimal CM { get; set; }
        public decimal YTD { get; set; }
        public decimal YTDM_1 { get; set; }

        //in vm input
        public decimal Budget { get; set; }
        public decimal Actual { get; set; }
        public decimal Variance { get; set; }
        public decimal Vol { get; set; }
        public decimal Mix { get; set; }
        public decimal Price { get; set; }
        public decimal MP { get; set; }
        public decimal FPP { get; set; }
        public decimal PRO { get; set; }
        public decimal PT { get; set; }
        public decimal Others { get; set; }
        public decimal Control { get; set; }

        internal List<ActualMergeRow> subrow { get; set; }
    }

    [Serializable]
    public class BudgetMergeRow : BaseMergeRow
    {
        public decimal L_Dec { get; set; }
        public decimal Jan { get; set; }
        public decimal Feb { get; set; }
        public decimal Mar { get; set; }
        public decimal Apr { get; set; }
        public decimal May { get; set; }
        public decimal Jun { get; set; }
        public decimal Jul { get; set; }
        public decimal Aug { get; set; }
        public decimal Sep { get; set; }
        public decimal Oct { get; set; }
        public decimal Nov { get; set; }
        public decimal Dec { get; set; }
    }

    [Serializable]
    public class ForecastMergeRow : BaseMergeRow
    {
        //forecast input
        public decimal M_1 { get; set; }
        public decimal M_2 { get; set; }
        public decimal M_3 { get; set; }
        public decimal FCST { get; set; }
        public decimal M_1_Qty { get; set; }
        public decimal M_1_T { get; set; }
        public decimal M_2_Qty { get; set; }
        public decimal M_2_T { get; set; }
        public decimal M_3_Qty { get; set; }
        public decimal M_3_T { get; set; }
    }


    public class InternalSale
    {
        public InternalSale()
        {
            add = false;
            convertT = string.Empty;
            exchangerate = true;
        }
        public string entity { get; set; }
        public string convertT { get; set; }
        public List<string> fields { get; set; }
        public bool add { get; set; }
        public bool exchangerate { get; set; }

        internal decimal Calc(decimal CMItemValue)
        {
            //expression = expression.Replace("{0}", CMItemValue + "");
            //string error = string.Empty;
            //string result = Evaluator.Eval(expression, out error);
            //return BusinessService.decimalParse(result);
            return 0.00M;
        }

        internal void Calc(List<CacheSPItem> InternalSalesItems, out decimal CMTmp, out decimal YTDM1Tmp, out decimal YTDTmp)
        {

            CMTmp = YTDM1Tmp = YTDTmp = 0.00M;
            var tmpInternalSales = InternalSalesItems;
            if (entity.ToUpper() != "ALL")
            {
                tmpInternalSales = InternalSalesItems.Where(p => p.CoName == entity).ToList();
            }
            foreach (string c in fields)
            {
                var row = new ActualMergeRow() { accountcode = c, exchangerate = exchangerate, convertT = convertT };
                CMTmp += tmpInternalSales.Sum(p => p.GetAfterRateValue(row, "CM", ""));
                YTDTmp += tmpInternalSales.Sum(p => p.GetAfterRateValue(row, "YTD", "YTD"));
            }

            if (!add)
            {
                CMTmp = -CMTmp;
                YTDTmp = -YTDTmp;
            }

           // YTDTmp = CMTmp + YTDM1Tmp;
        }

        internal void BudgetCalc(List<CacheSPItem> InternalSalesItems, string pro, string forceRateType, out decimal CMTmp)
        {
            CMTmp = 0.00M;
            var tmpInternalSales = InternalSalesItems;
            if (entity.ToUpper() != "ALL")
            {
                tmpInternalSales = InternalSalesItems.Where(p => p.CoName == entity).ToList();
            }
            foreach (string c in fields)
            {
                string fieldType = pro + "_" + c.Split('_')[0];
                string accountcode = c.Split('_')[1];

                var row = new ActualMergeRow() { accountcode = accountcode, exchangerate = exchangerate, convertT = convertT };
                CMTmp += tmpInternalSales.Sum(p => p.GetAfterRateValue(row, fieldType, forceRateType));
            }

            if (!add)
            {
                CMTmp = -CMTmp;
            }

        }
    }

    public class Process
    {
        public string Field { get; set; }
        public Exp Exp { get; set; }
    }

    public class Exp
    {
        public string mode { get; set; }
        public string start { get; set; }
        public string end { get; set; }
        public object equ { get; set; }
        public string[] fields { get; set; }
        public string addition { get; set; }
        public bool equexpression { get; set; }
    }

    public class ExchangeRate
    {
        /// <summary>
        /// Budget-Y : Means budget exchange rate in current year and current month
        /// Actual_N-1 : Means actual exchange rate in last year current month
        /// {Table}-{Mode}
        /// </summary>
        public string From { get; set; }

        public string LC { get; set; }
        public string FC { get; set; }

        public string YTD { get; set; }
        public string CM { get; set; }

        public string Budget_YTD { get; set; }
        public string Budget_CM { get; set; }

        public string M_1 { get; set; }
        public string M_2 { get; set; }
        public string M_3 { get; set; }

        public string YTDM_1 { get; set; }
        public string YTDM_2 { get; set; }
        public string YTDM_3 { get; set; }
        public string FCST { get; set; }

    }

    public class RateRole
    {
        public string table { get; set; }
        public string mode { get; set; }
        public string field { get; set; }
        public string skip { get; set; }
    }

}
